from django.test import SimpleTestCase
from django.contrib.auth.models import User
from unittest.mock import patch, MagicMock
from api.models import (
    UserProfile, Student, Teacher, StudentGroup, ReadingMaterial,
    SingleChoiceQuestion, MultipleChoiceQuestion, FillInBlankQuestion,
    Task, Experiment, StudentExperimentProgress, StudentAnswerRecord,
    ExperimentReport, TeacherEvaluation, ExperimentAnnouncement
)


class UserProfileModelTest(SimpleTestCase):
    @patch('api.models.UserProfile.objects.get')  # Mock UserProfile.objects.get
    @patch('django.contrib.auth.models.User.objects.create_user')  # Mock User创建
    def test_create_user_profile(self, mock_create_user, mock_profile_get):
        # 1. 构造Mock对象
        mock_user = MagicMock(spec=User)
        mock_user.username = 'testuser'
        mock_create_user.return_value = mock_user  # User.create_user返回Mock用户

        mock_profile = MagicMock(spec=UserProfile)
        mock_profile.role = UserProfile.ROLE_STUDENT
        mock_profile.is_student = True
        mock_profile_get.return_value = mock_profile  # UserProfile.get返回Mock Profile

        # 2. 执行测试逻辑
        user = User.objects.create_user(username='testuser', password='123456')
        profile = UserProfile.objects.get(user=user)

        # 3. 断言Mock交互和结果
        mock_create_user.assert_called_once_with(username='testuser', password='123456')
        mock_profile_get.assert_called_once_with(user=mock_user)
        self.assertEqual(profile.role, UserProfile.ROLE_STUDENT)
        self.assertTrue(profile.is_student)


class StudentModelTest(SimpleTestCase):
    @patch('api.models.Student.objects.create')  # Mock Student创建
    @patch('django.contrib.auth.models.User.objects.create_user')
    def test_create_student(self, mock_create_user, mock_student_create):
        # 构造Mock
        mock_user = MagicMock(spec=User)
        mock_user.username = 'stu1'
        mock_create_user.return_value = mock_user

        mock_student = MagicMock(spec=Student)
        mock_student.name = '张三'
        mock_student.user = mock_user
        mock_student_create.return_value = mock_student

        # 执行逻辑
        user = User.objects.create_user(username='stu1')
        student = Student.objects.create(user=user, name='张三', age=20, gender='男', email='stu1@test.com')

        # 断言
        mock_create_user.assert_called_once_with(username='stu1')
        mock_student_create.assert_called_once_with(
            user=mock_user, name='张三', age=20, gender='男', email='stu1@test.com'
        )
        self.assertEqual(student.name, '张三')
        self.assertEqual(student.user.username, 'stu1')


class TeacherModelTest(SimpleTestCase):
    @patch('api.models.Teacher.objects.create')
    @patch('django.contrib.auth.models.User.objects.create_user')
    def test_create_teacher(self, mock_create_user, mock_teacher_create):
        mock_user = MagicMock(spec=User)
        mock_user.username = 'teacher1'
        mock_create_user.return_value = mock_user

        mock_teacher = MagicMock(spec=Teacher)
        mock_teacher.name = '李老师'
        mock_teacher_create.return_value = mock_teacher

        user = User.objects.create_user(username='teacher1')
        teacher = Teacher.objects.create(user=user, name='李老师', email='t@test.com', department='物理')

        mock_teacher_create.assert_called_once_with(
            user=mock_user, name='李老师', email='t@test.com', department='物理'
        )
        self.assertEqual(teacher.name, '李老师')


class StudentGroupModelTest(SimpleTestCase):
    @patch('api.models.StudentGroup.objects.create')
    @patch('api.models.Teacher.objects.create')
    @patch('django.contrib.auth.models.User.objects.create_user')
    def test_create_group(self, mock_create_user, mock_teacher_create, mock_group_create):
        # 多层Mock：User → Teacher → StudentGroup
        mock_teacher_user = MagicMock(spec=User)
        mock_teacher_user.username = 'teacher2'
        mock_create_user.return_value = mock_teacher_user

        mock_teacher = MagicMock(spec=Teacher)
        mock_teacher.name = '王老师'
        mock_teacher_create.return_value = mock_teacher

        mock_group = MagicMock(spec=StudentGroup)
        mock_group.teacher = mock_teacher
        mock_group_create.return_value = mock_group

        # 执行逻辑
        teacher_user = User.objects.create_user(username='teacher2')
        teacher = Teacher.objects.create(user=teacher_user, name='王老师', email='w@test.com', department='化学')
        group = StudentGroup.objects.create(name='一组', teacher=teacher)

        # 断言
        mock_group_create.assert_called_once_with(name='一组', teacher=mock_teacher)
        self.assertEqual(group.teacher.name, '王老师')


# 以下模型测试逻辑类似，均通过Mock掉objects.create/get，避免真实数据库交互
class ReadingMaterialModelTest(SimpleTestCase):
    @patch('api.models.ReadingMaterial.objects.create')
    def test_create_material(self, mock_material_create):
        mock_material = MagicMock(spec=ReadingMaterial)
        mock_material.title = '材料1'
        mock_material_create.return_value = mock_material

        material = ReadingMaterial.objects.create(title='材料1', content='内容')
        mock_material_create.assert_called_once_with(title='材料1', content='内容')
        self.assertEqual(material.title, '材料1')


class SingleChoiceQuestionModelTest(SimpleTestCase):
    @patch('api.models.SingleChoiceQuestion.objects.create')
    def test_create_single_choice(self, mock_q_create):
        mock_q = MagicMock(spec=SingleChoiceQuestion)
        mock_q.correct_answer = 'B'
        mock_q_create.return_value = mock_q

        q = SingleChoiceQuestion.objects.create(
            text='1+1=?', option_a='1', option_b='2', option_c='3', option_d='4', correct_answer='B'
        )
        mock_q_create.assert_called_once_with(
            text='1+1=?', option_a='1', option_b='2', option_c='3', option_d='4', correct_answer='B'
        )
        self.assertEqual(q.correct_answer, 'B')


class MultipleChoiceQuestionModelTest(SimpleTestCase):
    @patch('api.models.MultipleChoiceQuestion.objects.create')
    def test_create_multiple_choice(self, mock_q_create):
        mock_q = MagicMock(spec=MultipleChoiceQuestion)
        mock_q.correct_answers = 'BD'
        mock_q_create.return_value = mock_q

        q = MultipleChoiceQuestion.objects.create(
            text='选择偶数', option_a='1', option_b='2', option_c='3', option_d='4', correct_answers='BD'
        )
        mock_q_create.assert_called_once_with(
            text='选择偶数', option_a='1', option_b='2', option_c='3', option_d='4', correct_answers='BD'
        )
        self.assertEqual(q.correct_answers, 'BD')


class FillInBlankQuestionModelTest(SimpleTestCase):
    @patch('api.models.FillInBlankQuestion.objects.create')
    def test_create_fill_in_blank(self, mock_q_create):
        mock_q = MagicMock(spec=FillInBlankQuestion)
        mock_q.correct_answer = '第五'
        mock_q_create.return_value = mock_q

        q = FillInBlankQuestion.objects.create(
            text='地球是第几大行星？', correct_answer='第五', fuzzy_answers='五,5'
        )
        mock_q_create.assert_called_once_with(
            text='地球是第几大行星？', correct_answer='第五', fuzzy_answers='五,5'
        )
        self.assertEqual(q.correct_answer, '第五')


class TaskModelTest(SimpleTestCase):
    @patch('api.models.Task.objects.create')
    def test_create_task(self, mock_task_create):
        mock_task = MagicMock(spec=Task)
        mock_task.name = '任务1'
        mock_task_create.return_value = mock_task

        task = Task.objects.create(name='任务1')
        mock_task_create.assert_called_once_with(name='任务1')
        self.assertEqual(task.name, '任务1')


class ExperimentModelTest(SimpleTestCase):
    @patch('api.models.Experiment.objects.create')
    @patch('django.contrib.auth.models.User.objects.create_user')
    def test_create_experiment(self, mock_create_user, mock_exp_create):
        mock_user = MagicMock(spec=User)
        mock_user.username = 'teacher3'
        mock_create_user.return_value = mock_user

        mock_exp = MagicMock(spec=Experiment)
        mock_exp.name = '实验1'
        mock_exp_create.return_value = mock_exp

        exp = Experiment.objects.create(name='实验1', created_by=mock_user)
        mock_exp_create.assert_called_once_with(name='实验1', created_by=mock_user)
        self.assertEqual(exp.name, '实验1')


class StudentExperimentProgressModelTest(SimpleTestCase):
    @patch('api.models.StudentExperimentProgress.objects.create')
    @patch('api.models.Experiment.objects.create')
    @patch('django.contrib.auth.models.User.objects.create_user')
    def test_create_progress(self, mock_create_user, mock_exp_create, mock_progress_create):
        mock_user = MagicMock(spec=User)
        mock_user.username = 'stu2'
        mock_create_user.return_value = mock_user

        mock_exp = MagicMock(spec=Experiment)
        mock_exp.name = '实验2'
        mock_exp_create.return_value = mock_exp

        mock_progress = MagicMock(spec=StudentExperimentProgress)
        mock_progress.is_completed = False
        mock_progress_create.return_value = mock_progress

        progress = StudentExperimentProgress.objects.create(student=mock_user, experiment=mock_exp)
        mock_progress_create.assert_called_once_with(student=mock_user, experiment=mock_exp)
        self.assertFalse(progress.is_completed)


class StudentAnswerRecordModelTest(SimpleTestCase):
    @patch('api.models.StudentAnswerRecord.objects.create')
    @patch('api.models.Task.objects.create')
    @patch('api.models.Experiment.objects.create')
    @patch('django.contrib.auth.models.User.objects.create_user')
    def test_create_answer_record(self, mock_create_user, mock_exp_create, mock_task_create, mock_record_create):
        mock_user = MagicMock(spec=User)
        mock_user.username = 'stu3'
        mock_create_user.return_value = mock_user

        mock_exp = MagicMock(spec=Experiment)
        mock_exp_create.return_value = mock_exp

        mock_task = MagicMock(spec=Task)
        mock_task_create.return_value = mock_task

        mock_record = MagicMock(spec=StudentAnswerRecord)
        mock_record.student = mock_user
        mock_record_create.return_value = mock_record

        record = StudentAnswerRecord.objects.create(student=mock_user, experiment=mock_exp, task=mock_task)
        mock_record_create.assert_called_once_with(student=mock_user, experiment=mock_exp, task=mock_task)
        self.assertEqual(record.student.username, 'stu3')


class ExperimentReportModelTest(SimpleTestCase):
    @patch('api.models.ExperimentReport.objects.create')
    @patch('api.models.Experiment.objects.create')
    @patch('django.contrib.auth.models.User.objects.create_user')
    def test_create_report(self, mock_create_user, mock_exp_create, mock_report_create):
        mock_user = MagicMock(spec=User)
        mock_user.username = 'stu4'
        mock_create_user.return_value = mock_user

        mock_exp = MagicMock(spec=Experiment)
        mock_exp_create.return_value = mock_exp

        mock_report = MagicMock(spec=ExperimentReport)
        mock_report.total_score = 95
        mock_report_create.return_value = mock_report

        report = ExperimentReport.objects.create(
            experiment=mock_exp, student=mock_user, total_score=95, task_scores={"1": 50, "2": 45}
        )
        mock_report_create.assert_called_once_with(
            experiment=mock_exp, student=mock_user, total_score=95, task_scores={"1": 50, "2": 45}
        )
        self.assertEqual(report.total_score, 95)


class TeacherEvaluationModelTest(SimpleTestCase):
    @patch('api.models.TeacherEvaluation.objects.create')
    @patch('api.models.Experiment.objects.create')
    @patch('django.contrib.auth.models.User.objects.create_user')
    def test_create_evaluation(self, mock_create_user, mock_exp_create, mock_eval_create):
        # Mock 学生用户、教师用户、实验
        mock_student = MagicMock(spec=User)
        mock_student.username = 'stu5'
        mock_teacher = MagicMock(spec=User)
        mock_teacher.username = 'teacher5'
        mock_create_user.side_effect = [mock_student, mock_teacher]  # 多次调用返回不同Mock

        mock_exp = MagicMock(spec=Experiment)
        mock_exp.name = '实验5'
        mock_exp_create.return_value = mock_exp

        mock_eval = MagicMock(spec=TeacherEvaluation)
        mock_eval.score = 90
        mock_eval_create.return_value = mock_eval

        eval_obj = TeacherEvaluation.objects.create(
            experiment=mock_exp, student=mock_student, teacher=mock_teacher, score=90, feedback='很好'
        )
        mock_eval_create.assert_called_once_with(
            experiment=mock_exp, student=mock_student, teacher=mock_teacher, score=90, feedback='很好'
        )
        self.assertEqual(eval_obj.score, 90)


class ExperimentAnnouncementModelTest(SimpleTestCase):
    @patch('api.models.ExperimentAnnouncement.objects.create')
    @patch('api.models.Experiment.objects.create')
    @patch('django.contrib.auth.models.User.objects.create_user')
    def test_create_announcement(self, mock_create_user, mock_exp_create, mock_ann_create):
        mock_teacher = MagicMock(spec=User)
        mock_teacher.username = 'teacher6'
        mock_create_user.return_value = mock_teacher

        mock_exp = MagicMock(spec=Experiment)
        mock_exp.name = '实验6'
        mock_exp_create.return_value = mock_exp

        mock_ann = MagicMock(spec=ExperimentAnnouncement)
        mock_ann.title = '通知'
        mock_ann_create.return_value = mock_ann

        ann = ExperimentAnnouncement.objects.create(
            experiment=mock_exp, title='通知', content='内容', created_by=mock_teacher
        )
        mock_ann_create.assert_called_once_with(
            experiment=mock_exp, title='通知', content='内容', created_by=mock_teacher
        )
        self.assertEqual(ann.title, '通知')